<?php
	if (!class_exists('MyDebuger'))
		die('Se requiere de la clase \'MyDebuger\' para utilizar la clase \'CPUStats\'');
	
	final class CPUStats extends MyDebuger {
		/* estado */
		private $cpuLoad	= Array();
		/* resultados */
		private $results	= Array();
		/* cantidad de CPUs */
		private $cpuCount	= 0;
		/* archivo */
		private $cpuFile	= Null;
		/* nombre del archivo */
		private $fileName	= '/proc/stat';
		/* bandera de chekeo */
		private $cheked	= False;
		
		/* constructor */
		public function __construct($nombre = 'CPULoad') {
			/* creamos el padre */
			parent::__construct($nombre);
		}
		
		/* destructor */
		public function __destruct() {
			/* mostramos un mensaje */
			$this->show('Desconectando del CPU..', False);
			/* matamos el padre */
			parent::__destruct();
			/* mostramos OK */
			$this->showOK();
		}
		
		public function getData($type = 'cpu') {
			try {
				/* verificamos si ya se cargaron los datos */
				if (!$this->cheked)
					/* cargamos el estado actual del procesador */
					$this->checkLoad();
				/* verificamos que el valor solicitado existe */
				$this->verificarSolicitud($type);
				/* retornamos el valor solicitado */
				return $this->results[$type];
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$this->show($e->getMensaje($this->html));
			}
		}
		
		private function verificarSolicitud($type) {
			/* mostramos un mensaje */
			$this->show('Verificando solicitud..', False);
			/* verificamos si existe la clave */
			if (!array_key_exists($type, $this->results))
				/* si no existe finalizamos con un error */
				$this->newException('Opcion incorrecta');
			/* mostramos OK al mensaje */
			$this->showOK();
		}
		
		private function checkLoad() {
			/* mostramos un mensaje */
			$this->show('Obteniendo datos', False); $this->show('..');
			/* otro mensaje */
			$this->show('Obteniendo estado actual..', False);
			/* obtenemos los datos del archivo */
			$this->getFileData(0);
			/* OK */
			$this->showOK();
			/* esperamos 1 segundo */
			sleep(1);
			/* otro mensaje */
			$this->show('Obteniendo estado actual..', False);
			/* volvemos a obtener los datos */
			$this->getFileData(1);
			/* OK */
			$this->showOK();
			/* procesamos los datos */
			$this->processData();
			/* modificamos la bandera */
			$this->cheked = True;
		}
		
		private function getFileData($time) {
			/* abrimos el archivo */
			$this->openFile();
			/* obtenemos los datos del archivo */
			$this->obtenerDatos($time);
			/* cerramos el archivo */
			$this->closeFile();
		}
		
		private function openFile() {
			/* verificamos si se tienen permisos sobre el archivo */
			$this->verificarPermisos();
			/* abrimos el archivo */
			$this->abrirArchivo();
		}
		
		private function verificarPermisos() {
			/* verificamos si existe */
			if (!file_exists($this->fileName))
				/* salimos con una excepcion */
				$this->newException('El fichero no existe');
			/* verificamos si se puede leer */
			if (!is_readable($this->fileName))
				/* salimos con una excepcion */
				$this->newException('El fichero no se puede leer');
		}
		
		private function abrirArchivo() {
			/* abrimos el archivo */
			$this->cpuFile = fopen($this->fileName, 'r');
		}
		
		private function obtenerDatos($time) {
			/* recorremos el archivo */
			while (!feof($this->cpuFile)) {
				/* obtenemos una linea del archivo y la limpiamos */
				$linea = $this->clean(fgets($this->cpuFile));
				/* verificamos si no es una linea vacia */
				if ($linea != '')
					/* almacenamos la linea */
					$this->cpuLoad[$time][] = explode(' ', $linea);
			}
		}
		
		private function closeFile() {
			/* cerramos el archivo */
			fclose($this->cpuFile);
		}
		
		private function processData() {
			/* mostramos un mensaje */
			$this->show('Procesando datos', False); $this->show('..');
			/* procesamos los datos de las CPUs */
			$this->procesarCPUs();
			/* procesamos los demas datos */
			$this->procesarDatos();
		}
		
		private function procesarCPUs() {
			/* obtenemos la cantidad de CPUs del sistema */
			$this->getCPUs();
			/* obtenemos el estado de cada CPU */
			$this->getCPUStates();
			/* eliminamos los datos que ya no son necesarios */
			$this->delUnnecessaryData();
		}
		
		private function getCPUs() {
			/* mostramos un mensaje */
			$this->show('Obteniendo cantidad de CPUs..', False);
			/* recorremos los resultados */
			while (strstr($this->cpuLoad[0][$this->cpuCount][0], 'cpu'))
				/* agregamos una CPU */
				$this->cpuCount++;
			/* eliminamos la ultima CPU de mas */
			$this->cpuCount--;
			/* almacenamos la cantidad de CPUs */
			$this->results['cpu_count'] = $this->cpuCount;
			/* OK */
			$this->showOK();
		}
		
		private function getCPUStates() {
			/* mostramos un mensaje */
			$this->show('Procesando estado de las CPUs..', False);
			/* recorremos las CPUs */
			for ($cpu = 0; $cpu <= $this->cpuCount; $cpu++) {
				/* almacenamos el resultado en el Array de resultados */
				$this->results[$this->cpuLoad[0][$cpu][0]] = $this->formulaCalculo(
					/* enviamos como array los tiempos */
					Array(
						/* tiempo inicial */
						$this->cpuLoad[0][$cpu],
						/* tiempo final */
						$this->cpuLoad[1][$cpu]
					), (
						/* si es la primera CPU */
						$cpu == 0 ?
						/* enviamos el rango en 100 por 100 */
						100 :
						/* sino, el rango es 100 por la cantidad de CPUs */
						(100 / $this->cpuCount)
					)
				);
				/* eliminamos la linea para alivianar memoria */
				unset(
					/* eliminamos el tiempo inicial */
					$this->cpuLoad[0][$cpu],
					/* eliminamos el tiempo final */
					$this->cpuLoad[1][$cpu]
				);
			}
			/* OK */
			$this->showOK();
		}
		
		private function formulaCalculo($tiempos, $porcentaje = 100) {
			/* Sumamos los 3 primeros valores */
			$T0 = $tiempos[0][1] + $tiempos[0][2] + $tiempos[0][3];
			$T1 = $tiempos[1][1] + $tiempos[1][2] + $tiempos[1][3];
			/* En otra variable agregamos el 4 valor */
			$U0 = $T0 + $tiempos[0][4];
			$U1 = $T1 + $tiempos[1][4];
			/* retornamos el valor de la formula */
			return ($porcentaje * (($T1 - $T0) / ($U1 - $U0)));
		}
		
		private function delUnnecessaryData() {
			/* eliminamos todo el array del tiempo inicial */
			unset($this->cpuLoad[0]);
		}
		
		private function procesarDatos() {
			/* mostramos un mensaje */
			$this->show('Procesando datos estadisticos..', False);
			/* obtenemos la cantidad de procesos bloqueados */
			$this->findProcesses(
				/* obtenemos la cantidad de procesos corriendo */
				$this->findProcesses(
					/* obtenemos la cantidad de procesos creados */
					$this->findProcesses(
						/* enviamos la posicion de la ultima linea del archivo */
						$this->cpuCount + 1,
						/* enviamos el valor se lo que deseamos obtener */
						'processes'
					),
					/* enviamos el valor de lo que deseamos obtener */
					'procs_running'
				),
				/* enviamos el valor de lo que deseamos obtener */
				'procs_blocked'
			);
			/* agregamos las propiedades del CPU */
			$this->agregarPropiedades();
			/* OK */
			$this->showOK();
		}
		
		private function findProcesses($linea, $busqueda) {
			/* recorremos linea por linea en busca de la que contenga la palabra 'processes' */
			while (!strstr($this->cpuLoad[1][$linea][0], $busqueda)) {
				/* si no es la linea la eliminamos */
				unset($this->cpuLoad[1][$linea]);
				/* y pasamos a la siguiente linea */
				$linea++;
			}
			/* almacenamos la cantidad de procesos corriendo */
			$this->results[$this->cpuLoad[1][$linea][0]] = $this->cpuLoad[1][$linea][1];
			/* eliminamos la linea original */
			unset($this->cpuLoad[1][$linea]);
			/* retornamos la posicion de la ultima linea recorrida */
			return ($linea + 1);
		}
		
		private function agregarPropiedades() {
			/* agregamos la arquitectura del CPU */
			$this->results['arch'] = php_uname('m');
		}
		
		private function clean($linea) {
			/* buscamos espacios dobles */
			while (strstr($linea, '  '))
				/* reemplazamos los espacios dobles */
				$linea = str_replace('  ', ' ', $linea);
			/* retornamos la linea limpia */
			return $linea;
		}
	}
?>